package main

import (
	"bufio"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	// "log"
	"fmt"
	"os"
	"runtime"
	"time"
)

var mqttclient mqtt.Client

func main() {
	println("Welcome to the mqtt client 1.0")
	print("\n")
	println("Copyright (c) 2016, IPP team. All rights reserved.")
	println("Source Repository: http://git.oschina.net/changhongipp/mqttclient.")
	print("\n")
	println("Type 'help;' or '\\h' for help.")

	commands()
}

func help() {
	println()
	println("List of all commands:")
	println("help      (\\h)  Display this help.")
	println("conn      (\\c)  Connect to the server. eg. conn host port. mqtt default port 1883")
	println("disconn   (\\c)  Disconnect the server.")
	println("sub       (\\s)  Subscribe message. eg. sub topic..")
	println("unsub     (\\us) Unsubscribe message. eg. unsub topic..")
	println("pub       (\\p)  Publish message. eg. pub topic message")
	println()
}

func conn(parame []string) {
	if mqttclient != nil && mqttclient.IsConnected() {
		println("error: connected")
		return
	}
	if len(parame) < 3 {
		println("eg. conn host port")
		return
	}
	host := parame[1]
	port := parame[2]
	broker := "tcp://emqttd@" + host + ":" + port
	// mqtt.DEBUG = log.New(os.Stdout, "", 0)
	// mqtt.ERROR = log.New(os.Stdout, "", 0)
	opts := mqtt.NewClientOptions().AddBroker(broker)
	opts.SetKeepAlive(2 * time.Second)
	opts.SetDefaultPublishHandler(messageHandler)
	opts.SetPingTimeout(1 * time.Second)

	mqttclient = mqtt.NewClient(opts)
	if token := mqttclient.Connect(); token.Wait() && token.Error() != nil {
		fmt.Printf("error: %v", token.Error())
		return
	}
}

func disconn() {
	if checkIsConnected() {
		mqttclient.Disconnect(250)
		mqttclient = nil
	}
}

func sub(parame []string) {
	if !checkIsConnected() {
		return
	}
	if len(parame) < 2 {
		println("eg. sub topic...")
		return
	}
	topics := parame[1:]
	for _, topic := range topics {
		if token := mqttclient.Subscribe(topic, 2, nil); token.Wait() && token.Error() != nil {
			fmt.Printf("error: %v", token.Error())
		}
	}
}

func unsub(parame []string) {
	if !checkIsConnected() {
		return
	}
	if len(parame) < 2 {
		println("eg. unsub topic...")
		return
	}
	topics := parame[1:]
	for _, topic := range topics {
		if token := mqttclient.Unsubscribe(topic); token.Wait() && token.Error() != nil {
			fmt.Printf("error: %v", token.Error())
		}
	}
}

func pub(parame []string) {
	if !checkIsConnected() {
		return
	}
	if len(parame) < 3 {
		println("eg. pub topic message.")
		return
	}
	message := ""
	end := parame[len(parame)-1]
	if parame[2][0] == '"' && end[len(end)-1] == '"' {
		for index := 2; index < len(parame); index++ {
			message += (parame[index] + " ")
		}
		message = message[1 : len(message)-2]
	} else {
		message = parame[2]
	}

	if token := mqttclient.Publish(parame[1], 2, false, message); token.Wait() && token.Error() != nil {
		fmt.Printf("error: %v", token.Error())
	}
}

func messageHandler(client mqtt.Client, msg mqtt.Message) {
	println()
	println("received message: ")
	println("topic:", msg.Topic(), "message:", string(msg.Payload()))
}

func checkIsConnected() bool {
	if mqttclient == nil || !mqttclient.IsConnected() {
		println("error: Not Connect")
		return false
	}
	return true
}

func commands() {
	println()
	for {
		print("mqtt> ")
		inputReader := bufio.NewReader(os.Stdin)
		inputString, err := inputReader.ReadString('\n')
		if err != nil {
			print(err.Error())
			os.Exit(1)
		} else {
			s := eraseNewLineChar(inputString)
			commands := splitCommands(s)
			if len(commands) == 0 {
				continue
			}
			command := commands[0]
			if command != " " && command != "" {
				switch command {
				case "help":
					help()
				case "conn":
					conn(commands)
				case "disconn":
					disconn()
				case "sub":
					sub(commands)
				case "unsub":
					unsub(commands)
				case "pub":
					pub(commands)
				default:
					println("[!] Unknown command: `", command, "`")
					println("Did you mean: help?")
					help()
				}
			}
		}
	}
}

func eraseNewLineChar(s string) string {
	osType := runtime.GOOS
	if osType == "windows" {
		return s[:len(s)-2]
	}
	return s[:len(s)-1]
}

func splitCommands(s string) []string {
	commandSlice := make([]string, 1)
	commandSlice[0] = ""
	subs := ""
	start := -1
	for index, c := range s {
		if c != ' ' {
			if start == -1 {
				start = index
			}
			if index == len(s)-1 {
				subs = s[start : index+1]
				start = -1
			}
		} else if c == ' ' && start > -1 {
			subs = s[start:index]
			start = -1
		}
		if subs != "" {
			commandSlice = append(commandSlice, subs)
			subs = ""
		}
	}
	return commandSlice[1:]
}
